/**
 * 20081230 1518: Grady.Kao, deleted method pedingZero(int:Value) and used java.text.DecimalFormat() to replace. <br>
 * 20081219 1049: Grady.Kao, deleted methd fingerArrayIndex() and used java.util.Arrays#binarySearch() to replace.
 */

package com.e885.util.string;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;

import com.e885.constant.util.DirectionConstant;
import com.e885.util.array.ArraysUtil;
import com.e885.util.numeric.RandomUtil;

/**
 * 有關字串的 util <br>
 * Copyright: Copyright (c) 2002-2007<br>
 * Company: E885 有限公司
 * 
 * @author Eric Gau(gau168.gau@msa.hinet.net)
 * @version 1.0
 */
public class StringUtil
{
	/**
	 * 身份證驗證程式
	 * 
	 * @param str 要驗證的身份證字串
	 * @return 如果驗證正確, 傳回 true, 否則傳回 false
	 */
	public static boolean checkIdentity( String str )
	{
		if( str == null )
			throw new IllegalArgumentException( "str is null" );

		// check length
		if( str.length() != 10 )
		{
			return false;
		}

		String[] codes =
			{ "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "O", "I" };

		int[] numbers =
			{ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 35, 34 };

		int total = 0;
		str = str.toUpperCase();
		String strFirst = str.substring( 0, 1 );
		String strNumber = str.substring( 1, 10 );

		// check format of first character
		if( !isLetter( strFirst ) )
		{
			return false;
		}

		// check format of number
		if( !isDigital( strNumber ) )
		{
			return false;
		}

		// check rule
		int lengthOfCodes = codes.length;
		for( int i = 0; i < lengthOfCodes; i++ )
		{
			if( strFirst.equals( codes[ i ] ) )
			{
				str = String.valueOf( numbers[ i ] ) + strNumber;
				break;
			}
		}

		for( int i = 10; i >= 1; i-- )
		{
			if( i == 10 )
			{
				total += Integer.parseInt( str.substring( 10 - i, 10 - i + 1 ) );
			}
			else
			{
				total += ( Integer.parseInt( str.substring( 10 - i, 10 - i + 1 ) ) * i );
			}
		}

		int value = 10 - total % 10;
		if( value == 10 )
		{
			value = 0;
		}

		return ( value == Integer.parseInt( strNumber.substring( 8, 9 ) ) );
	}

	/**
	 * 將字串陣列以分隔符號來組合, 如: computer,desk,room
	 * 
	 * @param source 字串陣列
	 * @param delim 分隔符號
	 * @return
	 */
	public static < T > String composeByArray( T[] source, String delim )
	{
		return composeByArray( source, delim, null );
	}

	/**
	 * 將字串陣列以分隔符號來組合, 並用引號包住, 如(使用雙引號): "computer","desk","room"
	 * 
	 * @param sources 字串陣列
	 * @param delim 分隔符號
	 * @param quote 引號
	 * @return
	 */
	public static < T > String composeByArray( T[] sources, String delim, String quote )
	{
		boolean isAddQuote = false;

		if( sources == null )
			return null;

		if( sources.length == 0 )
			return "";

		if( delim == null )
			throw new IllegalArgumentException( "delim is null" );

		if( quote != null )
			isAddQuote = true;

		StringBuffer buffer = new StringBuffer();

		if( isAddQuote )
			buffer.append( quote );

		buffer.append( sources[ 0 ].toString() );

		if( isAddQuote )
			buffer.append( quote );

		for( int i = 1; i < sources.length; i++ )
		{
			buffer.append( delim );

			if( isAddQuote )
				buffer.append( quote );

			buffer.append( sources[ i ].toString() );

			if( isAddQuote )
				buffer.append( quote );
		}

		return buffer.toString();
	}

	/**
	 * 取得一個由 0-9 亂數決定的字串, 如:長度(length) 指定為 3, 那回傳為 "285"
	 * 
	 * @param length int 要組合字串的長度
	 * @return String 回傳已組合的字串
	 */
	public static String composeDynamicNumber( int length )
	{
		return composeDynamicNumber( length, 10 );
	}

	/**
	 * 取得一個由 <code>number</code> 亂數決定的字串, 如 length: 3, number:4(0-3), 那麼回傳值可能為 "123", "333", "201" 等等
	 * 
	 * @param length 要組合字串的長度
	 * @param number 亂數決定數值的範圍, 但不能超過
	 * @return 回傳已組合的字串
	 */
	public static String composeDynamicNumber( int length, int number )
	{
		if( number > 10 || number < 1 )
		{
			throw new IllegalArgumentException( "number is error" );
		}

		StringBuffer buffer = new StringBuffer();

		for( int i = 0; i < length; i++ )
		{
			buffer.append( String.valueOf( RandomUtil.randomByInteger( number ) ) );
		}

		return buffer.toString();
	}

	/**
	 * 取得大量身份證字號
	 * 
	 * @param quantity int 取得的數量
	 * @return String[] 大量身份證字號
	 */
	public static String[] fetchQuantyIdentities( int quantity )
	{
		String[] identities = new String[ quantity ];

		for( int i = 0; i < quantity; i++ )
		{
			identities[ i ] = fetchRandomIdentity();
		}

		return identities;
	}

	/**
	 * 以亂數的方式, 取得身份證字號
	 * 
	 * @return String 身份證字號
	 */
	public static String fetchRandomIdentity()
	{
		StringBuffer buffer = new StringBuffer();

		String[] codes =
			{ "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "O", "I" };

		int[] numbers =
			{ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 35, 34 };

		buffer.append( numbers[ RandomUtil.randomByInteger( numbers.length ) ] );
		buffer.append( RandomUtil.randomByInteger( 2 ) + 1 );
		buffer.append( composeDynamicNumber( 7 ) );

		int value = Integer.parseInt( buffer.substring( 0, 1 ) );
		for( int i = 1; i < 10; i++ )
		{
			value += Integer.parseInt( buffer.substring( i, i + 1 ) ) * ( 10 - i );
		}

		value = value % 10;

		if( value == 0 )
		{
			value = 10;
		}

		buffer.append( ( 10 - value ) );
		int index = ArraysUtil.serach( numbers, Integer.parseInt( buffer.substring( 0, 2 ) ) );

		buffer.replace( 0, 2, codes[ index ] );

		return buffer.toString();
	}

	/**
	 * 以亂數取得性別, 男:M, 女:F
	 * 
	 * @return
	 */
	public static String fetchRandomSex()
	{
		if( RandomUtil.randomByInteger( 2 ) == 0 )
			return "M";
		else
			return "F";
	}

	/**
	 * 由指定的字串, 指定的次數, 重覆產生指定的字串成為字串<br>
	 * 如: count = 2, str = "test"<br>
	 * return string is "testtest"
	 * 
	 * @param count 重覆次數
	 * @param str 要產生的重覆字串
	 * @return 依重覆次數產生出來的字串
	 */
	public static String generateString( int count, String str )
	{
		if( str == null )
			throw new IllegalArgumentException( "str is null" );

		StringBuffer buffer = new StringBuffer();
		for( int i = 0; i < count; i++ )
		{
			buffer.append( str );
		}
		return buffer.toString();
	}

	/**
	 * 取得字串, 如果為 null 或空字串, 傳回 null
	 * 
	 * @param source
	 * @return
	 */
	public static String getString( String source )
	{
		if( source == null || source.length() == 0 )
			return null;

		return source;
	}

	/**
	 * 檢查字串的每個字元，是否為數字 0 到 9<br>
	 * 如: "520" return true<br>
	 * "5A0" return false
	 * 
	 * @param str 要檢查的字串
	 * @return false 表字串含有非數字 0 到 9 的字元
	 */
	public static boolean isDigital( String str )
	{
		if( str == null )
			throw new IllegalArgumentException( "str is null" );

		if( str.length() == 0 )
		{
			return false;
		}

		int lenghtoOfStr = str.length();

		for( int i = 0; i < lenghtoOfStr; i++ )
		{
			if( str.charAt( i ) < '0' || str.charAt( i ) > '9' )
			{
				return false;
			}
		}

		return true;
	}

	/**
	 * 檢查字串是否都為英文字母
	 * 
	 * @param str 要檢查的字串
	 * @return 如果是, 傳回 true, 否則傳 false
	 */
	public static boolean isLetter( String str )
	{
		if( str == null )
			throw new IllegalArgumentException( "str is null" );

		char ch;

		str = str.toUpperCase();

		int length = str.length();

		for( int i = 0; i < length; i++ )
		{
			ch = str.charAt( i );
			if( ch < 'A' || ch > 'Z' )
			{
				return false;
			}
		}

		return true;
	}

	/**
	 * 在字串前或後補上空白
	 * 
	 * @param source 要補空白的字串
	 * @param length 補完空白之後的字串長度
	 * @param pendingDirection 補上字元的位置(前方或後方)
	 * @return 已補完空白的字串
	 */
	public static String pendingSpace( String source, int length, int pendingDirection )
	{
		return pendingString( source, length, ' ', pendingDirection );
	}

	/**
	 * 在字串前或後補上指定的字元
	 * 
	 * @param source 要補指定字元的字串
	 * @param length 補完指定字元的字串
	 * @param pendingValue 指定字元,長度要為 1
	 * @param pendingDirection 補上字元的位置(前方或後方)
	 * @return 已補上字元的字串
	 */
	public static String pendingString( String source, int length, char pendingValue, int pendingDirection )
	{
		if( source == null )
			throw new IllegalArgumentException( "source is null" );

		if( length < 0 )
			throw new IllegalArgumentException( "the value of the length less than and equal to 0" );

		if( !( pendingDirection == DirectionConstant.FRONT || pendingDirection == DirectionConstant.BACK ) )
			throw new IllegalArgumentException( "the value of the direction is error" );

		int sourceLength = source.length();

		if( length < sourceLength )
			return source;

		for( int i = 0; i < length - sourceLength; i++ )
		{
			if( pendingDirection == DirectionConstant.FRONT )
				source = pendingValue + source;
			else
				source += pendingValue;
		}

		return source;
	}

	/**
	 * 在字串前或後補上 zero
	 * 
	 * @param str 要補 zero 的字串
	 * @param length 補完 zero 之後的字串長度
	 * @param pendingDirection 補上字元的位置(前方或後方)
	 * @return 已補完 zero 的字串
	 */
	public static String pendingZero( String str, int length, int pendingDirection )
	{
		return pendingString( str, length, '0', pendingDirection );
	}

	/**
	 * indexOf() 加強版,不檢查被引號括起來的字串, ex:<br>
	 * 1. source = "abc'de'fg", chekcValue = "d", return false<br>
	 * 2. source = "abcdefg", checkValue = "d", return true
	 * 
	 * @param source
	 * @param checkValue
	 * @param quote
	 * @return
	 */
	public static int indexOfExcludeQuote( String source, String checkValue, char quote )
	{
		if( source == null )
			throw new IllegalArgumentException( "source is null" );

		if( checkValue == null )
			throw new IllegalArgumentException( "source is null" );

		String strTemp = removeQuoteString( source, quote );

		int idx = strTemp.indexOf( checkValue );

		if( idx != -1 )
			return idx + ( source.length() - strTemp.length() );
		else
			return -1;
	}

	/**
	 * 分離字串, 如: source="t123tt45ttt6'tt'78tt, delim="t"<br>
	 * 1. if isIncludeDelim is true: return { "t", "123", "tt", "45", "ttt", "6'tt'78'", "tt" }<br>
	 * 2. if isIncludeDelim is false: return { "123", 45", "6'tt'78'" }
	 * 
	 * @param source
	 * @param delim
	 * @return
	 */
	public static String[] separate( String source, char delim, boolean isIncludeDelim )
	{
		if( source == null )
			throw new IllegalArgumentException( "source is null" );

		List< String > list = new ArrayList< String >();

		int length = source.length();

		StringBuffer buffer = new StringBuffer();

		boolean isQuoteScope = false;

		String str;

		String nextStr;

		for( int i = 0; i < length; i++ )
		{
			str = source.substring( i, i + 1 );

			buffer.append( str );

			if( ( i + 2 ) > length )
				nextStr = null;
			else
				nextStr = source.substring( i + 1, i + 2 );

			if( str.equals( "'" ) )
				isQuoteScope ^= true;

			// t12tt34'tt'tt
			// t->1: trigger
			// 2->t: trigger
			// t->3: trigger
			// '->t: triger
			if( nextStr != null && !isQuoteScope )
			{
				if( ( str.equals( String.valueOf( delim ) ) && !nextStr.equals( String.valueOf( delim ) ) )
					|| ( !str.equals( String.valueOf( delim ) ) && nextStr.equals( String.valueOf( delim ) ) ) )
					trigger( buffer, delim, list, isIncludeDelim );
			}
		}

		if( buffer.length() != 0 )
			trigger( buffer, delim, list, isIncludeDelim );

		return list.toArray( new String[ 0 ] );
	}

	/**
	 * 以 isIncludeDeim 和 mehtod isAllSameChars() 來決定是否把 buffe 的內容加到 list.
	 * 
	 * @param buffer
	 * @param delim
	 * @param list
	 * @param isIncludeDelim
	 */
	private static void trigger( StringBuffer buffer, char delim, List< String > list, boolean isIncludeDelim )
	{
		if( isIncludeDelim || !isAllSameChars( buffer.toString(), delim ) )
			list.add( buffer.toString() );

		buffer.delete( 0, buffer.length() );
	}

	/**
	 * 檢查字串是否為<code>c</cdoe> 字元所組成
	 * 
	 * @param source
	 * @param c
	 * @return
	 */
	public static boolean isAllSameChars( String source, char c )
	{
		int length = source.length();

		for( int i = 0; i < length; i++ )
			if( source.charAt( i ) != c )
				return false;

		return true;
	}

	/**
	 * 移除被<code>quote</code> 包起來的字串, 如 123'ab'45'cd' 變為 12345
	 * 
	 * @param source
	 * @param quote
	 * @return
	 */
	public static String removeQuoteString( String source, char quote )
	{
		StringBuffer buffer = new StringBuffer( source );

		int index = -1;

		while( ( index = buffer.indexOf( String.valueOf( quote ), index + 1 ) ) != -1 )
		{
			int secondIndex = buffer.indexOf( String.valueOf( quote ), index + 1 );

			if( secondIndex != -1 )
				buffer.delete( index, secondIndex + 1 );
		}

		return buffer.toString();
	}

	/**
	 * 將字串倒過來,在這裏間隔符號使用 " "
	 * 
	 * @param source 字串來源, ex:"This is a test"
	 * @return reverse 後的字串, ex:"test a is This"
	 */
	public static String reverse( String source )
	{
		return reverse( source, " " );
	}

	/**
	 * 將字串倒過來,在這裏間隔符號使用指定的符號 <code>delim</code>
	 * 
	 * @param source 字串來源, ex:"This<delim>is<delim>a<delim>test"
	 * @param delim 指定的間隔符號
	 * @return reverse 後的字串, ex:"test<delim>a<delim>is<delim>This"
	 */
	public static String reverse( String source, String delim )
	{
		if( source == null )
			throw new IllegalArgumentException( "source is null" );

		if( source.trim().length() == 0 )
			return "";

		if( delim == null || delim.length() == 0 )
			throw new IllegalArgumentException( "The argument of the delim is error" );

		Stack stack = new Stack();

		StringTokenizer tokenizer = new StringTokenizer( source, delim );

		while( tokenizer.hasMoreTokens() )
		{
			stack.push( tokenizer.nextToken() );
		}

		// 因為 stack 至少會有一個 element
		StringBuffer buffer = new StringBuffer( ( String )stack.pop() );

		while( !stack.empty() )
		{
			buffer.append( delim );
			buffer.append( ( String )stack.pop() );
		}

		return buffer.toString();
	}
}
